package com.clw.runtime;

import com.clw.operand.Operand;
import com.clw.operand.Operands;
import com.clw.runtime.VMStack.Frame;
import com.clw.runtime.VMStack.LocalVariable;
import com.clw.runtime.VMStack.OperandStack;
import com.clw.runtime.heap.ClassLoader;
import com.clw.runtime.heap.MethodArea.Method;
import com.clw.utils.PathUtil;
import com.sun.tools.classfile.Opcode;
import sun.tools.java.ClassPath;

/**
 * Created by chengliwei on 2019/1/13.
 */
public class Runner {

    public static void run() {
//        CommanderParser parser = new CommanderParser();
//        parser.addOption("-Xjre", "--Xjre", true, "specific jre");
//        parser.addOption("-cp", "--classpath", true, "specific time");
//        parser.parse(new String[]{"E:\\javaProjects\\toyvm\\target\\test-classes\\com\\clw\\classpath\\Test"});
//        String value = parser.getOptionValue("-Xjre");
        //String javaHome = System.getenv("JAVA_HOME");
        String javaHome = "E:\\Program Files\\Java\\jdk1.8.0_92";
        String jreLibPath = PathUtil.join(javaHome, "jre", "lib", "rt.jar");
        ClassPath cp = new ClassPath(
                "E:\\javaProjects\\toyvm\\target\\test-classes\\com\\clw\\classpath;" + jreLibPath);
        String className = "Test.class";
        Class clazz = loadClass(className, cp);
        Method main = getMainMethod(clazz);
        interpret(main);
    }

    private static void interpret(Method method) {
        byte[] code = method.getCode();
        int maxLocals = method.getMaxLocals();
        int maxStack = method.getMaxStack();
        Frame frame = new Frame(maxLocals, maxStack);
        VMStack vmStack = new VMStack();
        vmStack.push(frame);
        interpret(code, frame);
    }

    private static void interpret(byte[] code, Frame frame) {
        int length = code.length;
        for (int i = 0; i < length; i++) {
            Operand operand = transfer(code[i]);
            if(operand == null) {
                System.out.println(i + ":" + code[i]);
                break;
            }
            int dataLength = operand.dataLength();
            i = i + dataLength;
            byte[] data = new byte[dataLength];
            System.arraycopy(code, i + 1, data, 0, dataLength);
            operand.execute(data);
        }
//        for(byte c: code) {
//            Operand operand = transfer(c);
//            operand.execute(null);
//        }
    }

    private static Operand transfer(byte c) {
        String opCodeStr = Opcode.get((c + 256) % 256).name();
        System.out.println(opCodeStr);
        return Operands.getByName(opCodeStr);
    }

    private static Method getMainMethod(Class clazz) {
        for (Method method : clazz.getMethods()) {
            if (method.getName().equals("main")
                    && method.getDescriptor().equals("([Ljava/lang/String;)V")) {
                return method;
            }
        }
        return null;
    }

    private static Class loadClass(String className, ClassPath classPath) {
        ClassLoader classLoader = new ClassLoader(classPath);
        return classLoader.loadClass(className);
    }

    private static void testOperandStack(OperandStack operandStack) {

    }

    private static void testLocalVars(LocalVariable localVariable) {
        localVariable.setInt(12);
        localVariable.setFloat(13.0f);
        localVariable.setLong(14L);
        localVariable.setDouble(15.00);
//        localVariable.print();
        System.out.println(localVariable.getInt(0));
        System.out.println(localVariable.getFloat(1));
        System.out.println(localVariable.getLong(2));
        System.out.println(localVariable.getDouble(4));
    }


    public static void main(String[] args) {
        run();
    }
}
